package model;

import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import util.Funciones;
import util.readedFiles.Grabable;

public class Circunferencia extends Grabable {

	// private static final double TOL = 0.0000001;

	public Circunferencia(List<int[]> entrada) {
		super(entrada);
		this.procesarPuntos();
	}

	@Override
	public void generarSalida(int x) {
		String linea[] = { String.valueOf(x) };
		this.agregarLineaASalida(linea);
	}

	private void procesarPuntos() {
		List<int[]> entrada = this.getEntrada();
		List<Point2D.Double> puntos = new ArrayList<Point2D.Double>();
		for (int x = 0; x < entrada.size(); x++) {
			int[] is = entrada.get(x);
			if (is.length == 2) {
				Point2D.Double punto = new Point2D.Double(is[0], is[1]);
				puntos.add(punto);
			} else if (x != 0 && is.length == 1) {
				verificarPuntosCoCirculares(puntos);
				puntos = new ArrayList<Point2D.Double>();
			}
		}
	}

	private void verificarPuntosCoCirculares(List<Point2D.Double> puntos) {
		int max = 0;
		if (puntos.size() < 3)
			this.generarSalida(2);
		else {
			int cantidad = puntos.size();
			int coef = Funciones.coeficienteBinomial(cantidad, 3);
			List<Circulo> circulos = new ArrayList<Circulo>();
			List<Point2D.Double[]> combinacionesDePuntos = new ArrayList<Point2D.Double[]>();
			Random r = new Random();
			int x = 0;
			while (x < coef) {
				Point2D.Double p1 = puntos.get(r.nextInt(cantidad));
				Point2D.Double p2 = puntos.get(r.nextInt(cantidad));
				if (p2.equals(p1))
					p2 = puntos.get(r.nextInt(cantidad));
				else {
					Point2D.Double p3 = puntos.get(r.nextInt(cantidad));
					if (p3.equals(p1) || p3.equals(p2))
						p3 = puntos.get(r.nextInt(cantidad));
					else {
						Point2D.Double pto[] = new Point2D.Double[] { p1, p2, p3 };
						combinacionesDePuntos.add(pto);
						x++;
					}
				}
			}
			for (Point2D.Double[] pto : combinacionesDePuntos) {
				try {
					Circulo c = this.getCirculo(pto[0], pto[1], pto[2]);
					if (!circulos.contains(c))
						circulos.add(c);
				} catch (Exception e) {
					max = 2;
				}
			}
			int cantEnCirculo = 0;
			for (Circulo circulo : circulos) {
				for (Point2D.Double punto : puntos)
					if (circulo.contains(punto))
						cantEnCirculo++;
				if (cantEnCirculo > max)
					max = cantEnCirculo;
			}
			this.generarSalida(max);
		}
	}

	/*
	 * private void verificarPuntosCoCirculares(List<Point2D.Double> puntos) {
	 * if (puntos.size() < 3) this.generarSalida(puntos.size()); else if
	 * (puntos.size() == 3) { try { Point2D.Double centro =
	 * this.getCircuncentro(puntos.get(0), puntos.get(1), puntos.get(2)); double
	 * radio = this.getDistanciaEntrePuntos(centro, puntos.get(0));
	 * 
	 * @SuppressWarnings("unused") Circulo c = new Circulo(centro,
	 * radio);//this.circuloDesdePunto(puntos.get(0), puntos.get(1),
	 * puntos.get(2)); this.generarSalida(3); } catch (IllegalArgumentException
	 * e) { this.generarSalida(2); } } else { int max = 3; try { int cantidad =
	 * puntos.size(); int coeficienteBinomial = coeficienteBinomial(cantidad,
	 * max); List<Point2D.Double[]> puntosUsados = new
	 * ArrayList<Point2D.Double[]>(); int i = 0; Random r = new Random(); while
	 * (i < coeficienteBinomial) { Point2D.Double p1 =
	 * puntos.get(r.nextInt(cantidad)); Point2D.Double p2 =
	 * puntos.get(r.nextInt(cantidad)); if (!p2.equals(p1)) { Point2D.Double p3
	 * = puntos.get(r.nextInt(cantidad)); if (!p3.equals(p2) && !p3.equals(p1))
	 * { Point2D.Double[] pts = new Point2D.Double[3]; pts[0] = p1; pts[1] = p2;
	 * pts[2] = p3; if (!puntosUsados.contains(pts)) { puntosUsados.add(pts);
	 * i++; } } else { p3 = puntos.get(r.nextInt(cantidad)); } } else { p2 =
	 * puntos.get(r.nextInt(cantidad)); } } Map<Circulo, Integer> diccionario =
	 * new HashMap<Circulo, Integer>(); for (Double[] punto : puntosUsados) {
	 * try {
	 * 
	 * diccionario.put(circuloDesdePunto(punto[0], punto[1], punto[2]), 0); }
	 * catch (Exception e){ System.out.println(e.getMessage()); } } for (Double
	 * punto : puntos) { Iterator<Circulo> iterator =
	 * diccionario.keySet().iterator(); while (iterator.hasNext()) { Circulo c =
	 * iterator.next(); if (c.contains(punto)) { int val = diccionario.get(c) +
	 * 1; diccionario.put(c, val); } } } Iterator<Circulo> iterator =
	 * diccionario.keySet().iterator(); while (iterator.hasNext()) { int val =
	 * diccionario.get(iterator.next()); if (val > max) max = val; }
	 * this.generarSalida(max); } catch (IllegalArgumentException e) {
	 * this.generarSalida(2); } } }
	 */

	private double getDistanciaEntrePuntos(final Point2D.Double p1, final Point2D.Double p2) {
		double x1 = p1.getX();
		double y1 = p1.getY();
		double x2 = p2.getX();
		double y2 = p2.getY();
		double x = x2 - x1;
		double y = y2 - y1;
		return Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
	}

	private Circulo getCirculo(final Point2D.Double p1, final Point2D.Double p2, final Point2D.Double p3) throws ArithmeticException {
		Point2D.Double centro = this.getCircuncentro(p1, p2, p3);
		double radio = this.getDistanciaEntrePuntos(p1, centro);
		Circulo c = new Circulo(centro, radio);
		return c;
	}

	/********************************************************************************************/

	/**
	 * <a href=
	 * "http://stackoverflow.com/questions/4103405/what-is-the-algorithm-for-finding-the-center-of-a-circle-from-three-points"
	 * >fuente</a>
	 * 
	 * @param Point2D
	 *            .Double p1
	 * @param Point2D
	 *            .Double p2
	 * @param Point2D
	 *            .Double p3
	 * @return Circulo
	 */
	/*
	 * private Circulo circuloDesdePunto(final Point2D.Double p1, final
	 * Point2D.Double p2, final Point2D.Double p3) { final double offset =
	 * Math.pow(p2.getX(), 2) + Math.pow(p2.getY(), 2); final double bc =
	 * (Math.pow(p1.getX(), 2) + Math.pow(p1.getY(), 2) - offset) / 2.0; final
	 * double cd = (offset - Math.pow(p3.getX(), 2) - Math.pow(p3.getY(), 2)) /
	 * 2.0; final double det = (p1.getX() - p2.getX()) * (p2.getY() - p3.getY())
	 * - (p2.getX() - p3.getY()) * (p1.getY() - p2.getY());
	 * 
	 * if (Math.abs(det) < TOL) { throw new
	 * IllegalArgumentException("Point2D.Doubles alineados"); }
	 * 
	 * final double idet = 1 / det;
	 * 
	 * final double centrox = (bc * (p2.getY() - p3.getY()) - cd * (p1.getY() -
	 * p2.getY())) * idet; final double centroy = (cd * (p1.getX() - p2.getX())
	 * - bc * (p2.getX() - p3.getX())) * idet; final double radio =
	 * Math.sqrt(Math.pow(p2.getX() - centrox, 2) + Math.pow(p2.getY() -
	 * centroy, 2));
	 * 
	 * return new Circulo(new Point2D.Double(centrox, centroy), radio); }
	 */
	private Point2D.Double getCircuncentro(final Point2D.Double p1, final Point2D.Double p2, final Point2D.Double p3) throws ArithmeticException {
		Point2D.Double b = new Point2D.Double();
		Point2D.Double s = new Point2D.Double();
		double[][] mat = new double[3][3];
		// for (int x = 0; x < 3; x++) {
		// for (int y = 0; y < 3; y++) {
		// mat[x][y] = 0;
		// }
		// }
		for (int a = 0; a < 2; a++) {
			mat[a] = new double[3];
			mat[0] = new double[] { p1.x, p1.y, 1 };
			a = (int) det3x3(mat);
			mat[0][0] = Math.sqrt(p1.x) + Math.sqrt(p1.y);
			mat[1][0] = Math.sqrt(p2.x) + Math.sqrt(p2.y);
			mat[2][0] = Math.sqrt(p3.x) + Math.sqrt(p3.y);
			mat[0][1] = p1.y;
			mat[1][1] = p2.y;
			mat[2][1] = p3.y;
			b.x = -det3x3(mat);
			mat[0][1] = p1.x;
			mat[1][1] = p2.x;
			mat[2][1] = p3.x;
			b.y = det3x3(mat);
			s.x = -b.x / (2 * a);
			s.y = -b.y / (2 * a);
		}
		return s;
	}

	private double det3x3(double[][] Mat) {
		double v1, v2, v3, v4, v5, v6;
		v1 = Mat[0][0] * Mat[1][1] * Mat[2][2];
		v2 = Mat[0][2] * Mat[2][0] * Mat[2][1];
		v3 = Mat[0][1] * Mat[1][2] * Mat[2][0];
		v4 = Mat[0][2] * Mat[1][1] * Mat[2][0];
		v5 = Mat[0][0] * Mat[1][2] * Mat[2][1];
		v6 = Mat[0][1] * Mat[1][0] * Mat[2][2];
		return v1 + v2 + v3 - (v4 + v5 + v6);
	}

	private class Circulo {
		final Point2D.Double centro;
		final double radio;

		Circulo(Point2D.Double centro, double radio) {
			this.centro = centro;
			this.radio = radio;
		}

		@Override
		public String toString() {
			return new StringBuilder().append("Centro= ").append(centro).append(", radio=").append(radio).toString();
		}

		@Override
		public boolean equals(Object o) {
			if (o instanceof Circulo) {
				Circulo c = (Circulo) o;
				return this.centro.equals(c.centro) && this.radio == c.radio;
			}
			return false;
		}

		public boolean contains(Point2D.Double punto) {
			return punto.distance(this.centro.x, this.centro.y) == this.radio;
		}
	}

	public Point2D.Double getCircumcenter(Point2D.Double[] puntos) {
		/*
		 * Point2D.Double midpoint1 = new Point2D.Double((puntos[0].x +
		 * puntos[1].x) / 2, (puntos[0].y + puntos[1].y) / 2); Point2D.Double
		 * midpoint2 = new Point2D.Double((puntos[1].x + puntos[2].x) / 2,
		 * (puntos[1].y + puntos[2].y) / 2); Point2D.Double midpoint3 = new
		 * Point2D.Double((puntos[0].x + puntos[2].x) / 2, (puntos[0].y +
		 * puntos[2].y) / 2); double slope1 = (puntos[1].y -
		 * puntos[0].y)/(puntos[1].x - puntos[0].x); double slope2 =
		 * (puntos[2].y - puntos[1].y)/(puntos[2].x - puntos[1].x); double
		 * slope3 = (puntos[2].y - puntos[0].y)/(puntos[2].x - puntos[0].x);
		 * double perp1 = 1/slope1; double perp2 = 1/slope2; double perp3 =
		 * 1/slope3;
		 */
		double xa = puntos[0].x;
		double ya = puntos[0].y;
		double xb = puntos[1].x;
		double yb = puntos[1].y;
		double xc = puntos[2].x;
		double yc = puntos[2].y;
		double m = xa + xb;
		double m1 = m / 2;
		double m2 = ya + yb;
		double m3 = m2 / 2;
		double a = yb - ya;
		double a1 = xb - xa;
		double ab, ab1 = 0, ab2 = 0;
		String xx;
		if (a != 0 && a1 != 0) {
			ab = a / a1;
			ab1 = -1 / ab;
			ab2 = m3 - ab1 * m1;
			if (ab2 > 0)
				xx = "y" + "=" + ab1 + "x" + "+" + ab2;
			else
				xx = "y" + "=" + ab1 + "x" + ab2;
		}
		double m5 = xc + xb;
		double m6 = m5 / 2;
		double m7 = yc + yb;
		double m8 = m7 / 2;
		double b = yc - yb;
		double b1 = xc - xb;
		double bc, bc1 = 0, bc2 = 0;
		if (b != 0 && b1 != 0) {
			bc = b / b1;
			bc1 = -1 / bc;
			bc2 = (m8) - (bc1 * m6);
			if (bc2 > 0)
				xx = "y" + "=" + bc1 + "x" + "+" + bc2;
			else
				xx = "y" + "=" + bc1 + "x" + bc2;
		}
		double m9 = (+xc) + (+xa);
		double m10 = m9 / 2;
		double m11 = (+yc) + (+ya);
		double m12 = m11 / 2;
		double c = yc - ya;
		double c1 = xc - xa;
		double ca, ca1, ca2;
		if (c != 0 && c1 != 0) {
			ca = c / c1;
			ca1 = -1 / ca;
			ca2 = (m12) - (ca1 * m10);
			if (ca2 > 0)
				xx = "y" + "=" + ca1 + "x" + "+" + ca2;
			else
				xx = "y" + "=" + ca1 + "x" + ca2;
		}
		double con, con1, ad, ad1, di, di1, y8;
		if (a != 0 && a1 != 0 && b != 0 && b1 != 0 && c != 0 && c1 != 0) {
			con = -bc1;
			con1 = -bc2;
			ad = con + ab1;
			ad1 = ab2 + con1;
			di = ad1 / ad;
			di1 = -di;
			y8 = (bc1 * di1) + bc2;
			if(Math.round(di1) == (di1) && Math.round(y8) == (y8)){
				
			}
		}

	}

}
